/***************************************************************************
* Copyright  Faraday Technology Corp 2002-2003.  All rights reserved.      *
*--------------------------------------------------------------------------*
* Name:serial.c                                                            *
* Description: serial library routine                                      *
* Author:                                                        *
****************************************************************************/

#include <stdarg.h>
#include <stdio.h>

#include "unione_lite.h"
#include "DrvUART010.h"
#include "interrupt.h"

//UINT32 DebugSerialPort = DEFAULT_CONSOLE;//UART_FTUART010_3_PA_BASE;//DEFAULT_CONSOLE;
UINT32 UART_PORT[4]={UART_FTUART010_0_PA_BASE, UART_FTUART010_1_PA_BASE, UART_FTUART010_2_PA_BASE, UART_FTUART010_3_PA_BASE };

void fLib_SetSerialMode(DRVUART_PORT port_no, UINT32 mode)
{
	UINT32 mdr;
	
    mdr = inw(UART_PORT[port_no] + SERIAL_MDR);
    mdr &= ~SERIAL_MDR_MODE_SEL;
    outw(UART_PORT[port_no] + SERIAL_MDR, mdr | mode);
}

void fLib_EnableIRMode(DRVUART_PORT port_no, UINT32 TxEnable, UINT32 RxEnable)
{
	UINT32 acr;
	
    acr = inw(UART_PORT[port_no] + SERIAL_ACR);
    acr &= ~(SERIAL_ACR_TXENABLE | SERIAL_ACR_RXENABLE);
    if(TxEnable)
    	acr |= SERIAL_ACR_TXENABLE;
    if(RxEnable)
    	acr |= SERIAL_ACR_RXENABLE;
    outw(UART_PORT[port_no] + SERIAL_ACR, acr);
}

/*-----------------------------------------------------------------------------
  Function:		fLib_SerialInit                                                                         
                                                                                                         
  Parameter:        																					   	
	        	None                                     
  Returns:                                                                                                
               	None                                                                                      
  Description:                                                                                            
               	Initialize UART0, 38400bps, 8N1.                                    
 *-----------------------------------------------------------------------------*/

void fLib_SerialInit (DRVUART_PORT port_no, UINT32 baudrate, UINT32 parity,UINT32 num,UINT32 len)
{
	UINT32 lcr;

    lcr = inw(UART_PORT[port_no] + SERIAL_LCR) & ~SERIAL_LCR_DLAB;
	/* Set DLAB=1 */
    outw(UART_PORT[port_no] + SERIAL_LCR,SERIAL_LCR_DLAB);
    /* Set baud rate */
    outw(UART_PORT[port_no] + SERIAL_DLM, ((baudrate & 0xff00) >> 8)); //ycmo090930
    outw(UART_PORT[port_no] + SERIAL_DLL, (baudrate & 0xff));

	//clear orignal parity setting
	lcr &= 0xc0;
	
	switch (parity)
	{
		case PARITY_NONE:	
			//do nothing
    		break;
    	case PARITY_ODD:
		    lcr|=SERIAL_LCR_ODD;
   		 	break;
    	case PARITY_EVEN:
    		lcr|=SERIAL_LCR_EVEN;
    		break;
    	case PARITY_MARK:
    		lcr|=(SERIAL_LCR_STICKPARITY|SERIAL_LCR_ODD);
    		break;
    	case PARITY_SPACE:
    		lcr|=(SERIAL_LCR_STICKPARITY|SERIAL_LCR_EVEN);
    		break;
    
    	default:
    		break;
    }
    
    if(num==2)
		lcr|=SERIAL_LCR_STOP;
	
	len-=5;
	
	lcr|=len;	
    
    outw(UART_PORT[port_no]+SERIAL_LCR,lcr);    
}

void fLib_SetUartClkDiv(int uart_port, int div_m, int div_n)
{
  int RegVal;

  //set uart uclk to 90.112MHz, it is default to 49.152MHz
  //RegVal = READ(0x41000824);
  //RegVal |= (0x3 << ((uart_port * 2) + 12));
  //WRITE(0x41000824, RegVal);

  /*m = 3, n = 5, uclk(49.152MHz)/div = 14838339 for 921600
  m = 2, n = 0, uclk(49.152MHz)/div = 24576000 for 1500000
  m = 2, n = 7, uclk(90.112MHz)/div = 36969025 for 2304000*/

  RegVal = READ(0x41000050);
  RegVal &= ~0x00400000;
  WRITE(0x41000050, RegVal);

  RegVal = READ(0x41000828);
  RegVal &= ~(0xff << (uart_port * 8));
  RegVal |= (((div_n << 4) | div_m) << (uart_port * 8));
  WRITE(0x41000828, RegVal);

  RegVal = READ(0x41000050);
  RegVal |= 0x00400000;
  WRITE(0x41000050, RegVal);
}

void fLib_SetSerialLoopback(DRVUART_PORT port_no, UINT32 onoff)
{
	UINT32 temp;

	temp=inw(UART_PORT[port_no]+SERIAL_MCR);
	if(onoff==ON)	
		temp|=SERIAL_MCR_LPBK;
	else
		temp&=~(SERIAL_MCR_LPBK);
		
	outw(UART_PORT[port_no]+SERIAL_MCR,temp);	
}

void fLib_SetSerialFifoCtrl(DRVUART_PORT port_no, UINT32 level_tx, UINT32 level_rx, UINT32 resettx, UINT32 resetrx)  //V1.20//ADA10022002
{
	UINT8 fcr = 0;
 
 	fcr |= SERIAL_FCR_FE;
 
 	switch(level_rx)  //V1.20//ADA10022002//Start
 	{
 		case 4:
 			fcr|=0x40;
 			break;
 		case 8:
 			fcr|=0x80;
 			break;
 		case 14:
 			fcr|=0xc0;
 			break;
 		default:
 			break;
 	}
  //V1.20//ADA10022002//Start
 	switch(level_tx)
 	{
 		case 3:
 			fcr|=0x01<<4;
 			break;
 		case 9:
 			fcr|=0x02<<4;
 			break;
 		case 13:
 			fcr|=0x03<<4;
 			break;
 		default:
 			break;
 	}
  //V1.20//ADA10022002//End 	
	if(resettx)
		fcr|=SERIAL_FCR_TXFR;

	if(resetrx)
		fcr|=SERIAL_FCR_RXFR; 	

	outw(UART_PORT[port_no]+SERIAL_FCR,fcr);
}


void fLib_DisableSerialFifo(DRVUART_PORT port_no)
{
	outw(UART_PORT[port_no]+SERIAL_FCR,0);
}


void fLib_SetSerialInt(DRVUART_PORT port_no, UINT32 IntMask)
{
	outw(UART_PORT[port_no] + SERIAL_IER, IntMask);
}

char fLib_GetSerialChar(DRVUART_PORT port_no)
{   
    char Ch;    
	UINT32 status;
	
   	do
	{
	 	status=inw(UART_PORT[port_no]+SERIAL_LSR);
	}
	while (!((status & SERIAL_LSR_DR)==SERIAL_LSR_DR));	// wait until Rx ready
    Ch = inw(UART_PORT[port_no] + SERIAL_RBR);    
    return (Ch);
}				

void fLib_PutSerialChar(DRVUART_PORT port_no, char Ch)
{
  	UINT32 status;
  
    do
	{
	 	status=inw(UART_PORT[port_no]+SERIAL_LSR);
	}while (!((status & SERIAL_LSR_THRE)==SERIAL_LSR_THRE));	// wait until Tx ready	   
    outw(UART_PORT[port_no] + SERIAL_THR,Ch);
}

void fLib_PutSerialStr(DRVUART_PORT port_no, char *Str)
{
  	char *cp;
   
 	for(cp = Str; *cp != 0; cp++)       
   		fLib_PutSerialChar(port_no, *cp);	
}


void fLib_Modem_waitcall(DRVUART_PORT port_no)
{
	fLib_PutSerialStr(port_no, "ATS0=2\r");	
}					

void fLib_Modem_call(DRVUART_PORT port_no, char *tel)
{
	fLib_PutSerialStr(port_no, "ATDT");
	fLib_PutSerialStr(port_no,  tel);
	fLib_PutSerialStr(port_no, "\r");
}
#if 0
int32_t fLib_Modem_getchar(DRVUART_PORT port_no,int TIMEOUT)
{
  	UINT64 start_time, middle_time, dead_time;
  	UINT32 status;
	INT8 ch;
	UINT32 n=0;

  	start_time = fLib_CurrentT1Tick();
  	dead_time = start_time + TIMEOUT;
  
 	do
	{			
		if(n>1000)
		{
			middle_time = fLib_CurrentT1Tick();
			if (middle_time > dead_time)
				return 0x100;
		}			
		status = inw(UART_PORT[port_no] + SERIAL_LSR);	    
		n++;
	}while (!((status & SERIAL_LSR_DR)==SERIAL_LSR_DR));	
		  
    ch = inw(UART_PORT[port_no] + SERIAL_RBR);    
    return (ch);
}

BOOL fLib_Modem_putchar(DRVUART_PORT port_no, INT8 Ch)
{
	UINT64 start_time, middle_time, dead_time;
  	UINT32 status;
	UINT32 n=0;
	  
  	start_time = fLib_CurrentT1Tick();
  	dead_time = start_time + 5;
  	  
	do
	{
		if(n>1000)
		{
			middle_time = fLib_CurrentT1Tick();
			if (middle_time > dead_time)
				return FALSE;
		}	
		status = inw(UART_PORT[port_no] + SERIAL_LSR);	    
		n++;
	}while (!((status & SERIAL_LSR_THRE)==SERIAL_LSR_THRE));	
	  
	outw(UART_PORT[port_no] + SERIAL_THR, Ch);
	    
	return TRUE;
}
#endif
void fLib_EnableSerialInt(DRVUART_PORT port_no, UINT32 mode)
{
	UINT32 data;

	data = inw(UART_PORT[port_no] + SERIAL_IER);
	outw(UART_PORT[port_no] + SERIAL_IER, data | mode);
}


void fLib_DisableSerialInt(DRVUART_PORT port_no, UINT32 mode)
{
UINT32 data;

	data = inw(UART_PORT[port_no] + SERIAL_IER);
	mode = data & (~mode);	
	outw(UART_PORT[port_no] + SERIAL_IER, mode);
}

UINT32 fLib_ReadSerialIER(DRVUART_PORT port_no)
{
	return inw(UART_PORT[port_no] + SERIAL_IER);
}

UINT32 fLib_SerialIntIdentification(DRVUART_PORT port_no)
{
	return inw(UART_PORT[port_no] + SERIAL_IIR);
}

void fLib_SetSerialLineBreak(DRVUART_PORT port_no)
{
UINT32 data;

	data = inw(UART_PORT[port_no] + SERIAL_LCR);
	outw(UART_PORT[port_no] + SERIAL_LCR, data | SERIAL_LCR_SETBREAK);
}

void fLib_SerialRequestToSend(DRVUART_PORT port_no)
{
UINT32 data;

	data = inw(UART_PORT[port_no] + SERIAL_MCR);
	outw(UART_PORT[port_no] + SERIAL_MCR, data | SERIAL_MCR_RTS);
}

void fLib_SerialStopToSend(DRVUART_PORT port_no)
{
UINT32 data;

	data = inw(UART_PORT[port_no] + SERIAL_MCR);
	data &= ~(SERIAL_MCR_RTS);	
	outw(UART_PORT[port_no] + SERIAL_MCR, data);
}

void fLib_SerialDataTerminalReady(DRVUART_PORT port_no)
{
UINT32 data;

	data = inw(UART_PORT[port_no] + SERIAL_MCR);
	outw(UART_PORT[port_no] + SERIAL_MCR, data | SERIAL_MCR_DTR);
}

void fLib_SerialDataTerminalNotReady(DRVUART_PORT port_no)
{
UINT32 data;

	data = inw(UART_PORT[port_no] + SERIAL_MCR);
	data &= ~(SERIAL_MCR_DTR);	
	outw(UART_PORT[port_no] + SERIAL_MCR, data);
}

UINT32 fLib_ReadSerialLineStatus(DRVUART_PORT port_no)
{
	return inw(UART_PORT[port_no] + SERIAL_LSR);
}

UINT32 fLib_ReadSerialModemStatus(DRVUART_PORT port_no)
{
	return inw(UART_PORT[port_no] + SERIAL_MSR);
}
// End of file - serial.c


//used for CLI
//#define CLI_PORT 	DebugSerialPort

UINT32 GetUartStatus(DRVUART_PORT port_no)
{
    UINT32 status; 
    status=inw(UART_PORT[port_no]+SERIAL_LSR);
    return status; 
}


UINT32 IsThrEmpty(UINT32 status)
{
    if((status & SERIAL_LSR_THRE)==SERIAL_LSR_THRE)
        return 1;
    else
        return 0;  
}

UINT32 IsDataReady(UINT32 status)
{
    if((status & SERIAL_IER_DR)==SERIAL_IER_DR)
        return 1;
    else
        return 0;  

}

void CheckRxStatus(DRVUART_PORT port_no)
{
    UINT32 Status;
    do
    {
		Status = GetUartStatus(port_no);
	}
	while (!IsDataReady(Status));	// wait until Rx ready
}

void CheckTxStatus(DRVUART_PORT port_no)
{   
    UINT32 Status;
	do
    {
	    Status = GetUartStatus(port_no);	    
    }while (!IsThrEmpty(Status));	// wait until Tx ready	   
}
UINT32 fLib_kbhit(DRVUART_PORT port_no)
{
  UINT32 Status;
	
	Status = GetUartStatus(port_no);
	if(IsDataReady(Status))
		return 1;
	else
		return 0;
}

char fLib_getch(DRVUART_PORT port_no)
{   
    char ch;    
	
	if(fLib_kbhit(port_no))
    	ch=inw(UART_PORT[port_no]+SERIAL_RBR);    
   	else
   		ch=0;
   		
    return ch;
}	

int fLib_PollingData(DRVUART_PORT port_no) {
  UINT32 status;
  status = inw(UART_PORT[port_no]+SERIAL_LSR);
  return (status & SERIAL_LSR_DR) == SERIAL_LSR_DR;
}

char fLib_getchar(DRVUART_PORT port_no)
{   
    char Ch;    
    CheckRxStatus(port_no);
    Ch = inw(UART_PORT[port_no]+SERIAL_RBR);    
    return (Ch);
}				

char fLib_getchar_timeout(DRVUART_PORT port_no, unsigned long timeout)
{
	char		Ch;

	/* wait until rx status ready */
	while((GetUartStatus(port_no) & SERIAL_IER_DR) == 0x0)
	{
		if(timeout != 0)/* 0 means never timeout */
		{
			timeout --;/* count down timeout value */
			if(timeout == 0)/* 0 means timeout */
			{
				return (0xFF);/* return timeout value */
			}
		}
	}
	/* return rx character */
	Ch = inw(UART_PORT[port_no]+SERIAL_RBR);
	return (Ch);
}


void fLib_putchar(DRVUART_PORT port_no, char Ch)
{
    if(Ch!='\0')
    {        
        CheckTxStatus(port_no);
        outw(UART_PORT[port_no]+SERIAL_THR,Ch);
    }
    
    if (Ch == '\n')
    {
	    CheckTxStatus(port_no);
        outw(UART_PORT[port_no]+SERIAL_THR,'\r');
    }    
}

void fLib_sendchar(DRVUART_PORT port_no, char Ch) {
  CheckTxStatus(port_no);
  outw(UART_PORT[port_no]+SERIAL_THR, Ch);
}

void fLib_putc(DRVUART_PORT port_no, char Ch)
{
    CheckTxStatus(port_no);
    outw(UART_PORT[port_no]+SERIAL_THR,Ch);
    
    if (Ch == '\n')
    {
	    CheckTxStatus(port_no);
        outw(UART_PORT[port_no]+SERIAL_THR,'\r');
    }      
}


void fLib_putstr(DRVUART_PORT port_no, char *str)
{
    char *cp;   
    for(cp = str; *cp != 0; cp++)       
        fLib_putchar(port_no, *cp);
}

static char g_print_buffer[1024] = {0};
void fLib_printf(const char *f, ...)	/* variable arguments */
{
    va_list arg_ptr;    
    UINT32 i;
//    unsigned long flags;
      
	//spin_lock_irqsave(NULL, flags);
  // i=strlen(f);
 //  if(i>254)
 //  {
 //     while(1);
//   }
   
   	//put the character to buffer
   	va_start(arg_ptr, f);
   	vsnprintf(&g_print_buffer[0], sizeof(g_print_buffer), f, arg_ptr);
   	va_end(arg_ptr);   
	
   	//output the buffer
    i=0;
    //while((buffer[i])&&(i<255))
    while(g_print_buffer[i])
    {
       if('\n' == g_print_buffer[i]){
           fLib_putchar(DEBUG_CONSOLE, '\r');
       }
    	fLib_putchar(DEBUG_CONSOLE, g_print_buffer[i]);
    	i++;
    }
   /* restore the previous mode */
   //spin_unlock_irqrestore(NULL, flags);

   // return i;
}


//int fLib_scanf(char *buf)
INT32 fLib_gets(DRVUART_PORT port_no, char *buf)
{
    char    *cp;
    char    data;
    UINT32  count;
    count = 0;
    cp = buf;
    
    do
    {
        data = fLib_getchar(port_no);

        switch(data)
        {
            case RETURN_KEY:
                if(count < 256)
                {
                    *cp = '\0';
                    fLib_putchar(port_no, '\n');
                }          
                break;
            case BACKSP_KEY:
            case DELETE_KEY:
                if(count)
                {
                    count--;
                    *(--cp) = '\0';
                    fLib_putstr(port_no, "\b \b");
                }         
                break;
            default:         
                if( data > 0x1F && data < 0x7F && count < 256)
                {
                    *cp = (char)data;
                    cp++;
                    count++;
                    fLib_putchar(port_no, data);
                }
                break;
        }
    } while(data != RETURN_KEY);  
    
  return (count);
}


//for SWI call
void fLib_DebugPrintChar(DRVUART_PORT port_no, char ch)
{
    if(ch != '\0' && ch != '\n')
    {
		  fLib_PutSerialChar(port_no, ch);
    }
    else if(ch == '\n')
    {
  	  fLib_PutSerialChar(port_no, '\r');//CR
      fLib_PutSerialChar(port_no, '\n');//LF
    }
}

void fLib_DebugPrintString(DRVUART_PORT port_no, char *str)
{
	while(*str)
	{
		fLib_DebugPrintChar(port_no, *str);
		str++;
	}
}

char fLib_DebugGetChar(DRVUART_PORT port_no)
{
	return fLib_GetSerialChar(port_no);
}

UINT32 fLib_DebugGetUserCommand(DRVUART_PORT port_no, UINT8 * buffer, UINT32 Len)
{
    int offset = 0, c;

    buffer[0] = '\0';
    while (offset < (Len - 1)) {
	c = fLib_GetSerialChar(port_no);

	if (c == '\b')		//backspace
	{
	    if (offset > 0) {
		// Rub out the old character & update the console output
		offset--;
		buffer[offset] = 0;

		fLib_DebugPrintString(port_no, "\b \b");
	    }
	} else if (c == DELETE_KEY)	//backspace
	{
	    if (offset > 0) {
		// Rub out the old character & update the console output
		offset--;
		buffer[offset] = 0;

		fLib_DebugPrintString(port_no, "\b \b");
	    }
	}

	else {
	    if (c == '\r')
		c = '\n';	// treat \r as \n

	    fLib_PutSerialChar(port_no, c);

	    buffer[offset++] = c;

	    if (c == '\n')
		break;
	}
    }

    buffer[offset] = '\0';

    return offset;
}

UINT32 fLib_UartIRQInit(int uartIRQNum, void* irqhandler)
{
    fLib_printf("enter uart_irq!\r\n");
    switch (uartIRQNum) {
      case UART_FTUART010_0_IRQ:
        outw(UART_FTUART010_0_PA_BASE + SERIAL_IER, 0x1);
        break;
      case UART_FTUART010_1_IRQ:
        outw(UART_FTUART010_1_PA_BASE + SERIAL_IER, 0x1);
        outw(UART_FTUART010_1_PA_BASE + SERIAL_LCR, 0x3);
        outw(UART_FTUART010_1_PA_BASE + SERIAL_FCR, 0x1);
        break;
      case UART_FTUART010_2_IRQ:
        outw(UART_FTUART010_2_PA_BASE + SERIAL_IER, 0x1);
        break;
      case UART_FTUART010_3_IRQ:
        outw(UART_FTUART010_3_PA_BASE + SERIAL_IER, 0x1);
        break;
      default:
        break;
    }
    if(fLib_ConnectInt(uartIRQNum, irqhandler) == 0){
        fLib_printf("Fail to register uart_isr \r\n");
        return -1;
    }
    fLib_SetIntTrig(uartIRQNum,0,0);//level,high
    fLib_EnableInt(uartIRQNum);
    return 0;
}

